﻿using System;
using System.Diagnostics;

namespace Simp.Model.CoreExpressions
{
	/// <summary>Base class for expressions taking a left and right numeric expressions (e.g. addition).</summary>
	[ParameterPolicy("left", "right")]
	[DebuggerDisplay("{ExpressionHelper.Serialize(this)}")]
	public abstract class BinaryNumericExpressionBase : IExpression, ISimplifiableExpression, IExpressionSerializer
	{
		#region ISimplifiableExpression Members
		IExpression ISimplifiableExpression.Simplify(SimplificationMode simplificationMode, params IExpression[] parameterList)
		{
			if (parameterList == null)
			{
				throw new ArgumentNullException("parameterList");
			}
			if (parameterList.Length == 2)
			{
				IExpression leftExpression = parameterList[0];
				IExpression rightExpression = parameterList[1];
				IExpression inOrderExpression=Simplify(simplificationMode, leftExpression, rightExpression);

				return inOrderExpression == null && IsCommuting
					? Simplify(simplificationMode, rightExpression, leftExpression)
					: inOrderExpression;
			}
			else
			{
				return null;
			}
		}
		#endregion

		#region IExpressionSerializer Members
		void IExpressionSerializer.Serialize(AppendOnlyStringBuilder builder, params IExpression[] parameters)
		{
			if (builder == null)
			{
				throw new ArgumentNullException("builder");
			}
			if (parameters == null)
			{
				throw new ArgumentNullException("parameters");
			}
			if (parameters.Length != 2)
			{
				ExpressionHelper.SerializeGeneric(builder, this, parameters);
			}
			else
			{
				builder.Append("(");
				ExpressionHelper.Serialize(builder, parameters[0]);
				builder.Append(" ");
				builder.Append(OperationSymbol);
				builder.Append(" ");
				ExpressionHelper.Serialize(builder, parameters[1]);
				builder.Append(")");
			}
		}
		#endregion

		/// <summary>
		/// Called by <see cref="ISimplifiableExpression.Simplify"/>.  Default implementation checks for numerics
		/// and calls <see cref="SimplifyNumerics"/> if both left and right expressions are numerics.
		/// </summary>
		/// <param name="simplificationMode"></param>
		/// <param name="left"></param>
		/// <param name="right"></param>
		/// <returns></returns>
		protected virtual IExpression Simplify(
			SimplificationMode simplificationMode,
			IExpression left,
			IExpression right)
		{
			INumericExpression leftNumeric = left as INumericExpression;
			INumericExpression rightNumeric = right as INumericExpression;

			if (leftNumeric != null && rightNumeric != null)
			{
				return SimplifyNumerics(
					simplificationMode,
					leftNumeric.GetInteger(),
					leftNumeric.GetRational(),
					leftNumeric.GetReal(simplificationMode == SimplificationMode.AllowApproximation),
					rightNumeric.GetInteger(),
					rightNumeric.GetRational(),
					rightNumeric.GetReal(simplificationMode == SimplificationMode.AllowApproximation));
			}
			else
			{
				return null;
			}
		}

		/// <summary>Implemented by derived expressions:  returns a simplification.</summary>
		/// <remarks>Called only when both left and right expressions a numerics.</remarks>
		/// <param name="simplificationMode"></param>
		/// <param name="leftInteger"></param>
		/// <param name="leftRational"></param>
		/// <param name="leftReal"></param>
		/// <param name="rightInteger"></param>
		/// <param name="rightRational"></param>
		/// <param name="rightReal"></param>
		/// <returns></returns>
		protected abstract IExpression SimplifyNumerics(
			SimplificationMode simplificationMode,
			IntegerExpression leftInteger,
			RationalExpression leftRational,
			RealExpression leftReal,
			IntegerExpression rightInteger,
			RationalExpression rightRational,
			RealExpression rightReal);

		/// <summary>Returns <c>true</c> iif the operation commutes.</summary>
		protected abstract bool IsCommuting { get; }

		/// <summary>Returns the symbol of the operation.</summary>
		protected abstract string OperationSymbol { get; }
	}
}